-- C761010.A
--
--                             Grant of Unlimited Rights
--
--     The Ada Conformity Assessment Authority (ACAA) holds unlimited
--     rights in the software and documentation contained herein. Unlimited
--     rights are the same as those granted by the U.S. Government for older
--     parts of the Ada Conformity Assessment Test Suite, and are defined
--     in DFAR 252.227-7013(a)(19). By making this public release, the ACAA
--     intends to confer upon all recipients unlimited rights equal to those
--     held by the ACAA. These rights include rights to use, duplicate,
--     release or disclose the released technical data and computer software
--     in whole or in part, in any manner and for any purpose whatsoever, and
--     to have or permit others to do so.
--
--                                    DISCLAIMER
--
--     ALL MATERIALS OR INFORMATION HEREIN RELEASED, MADE AVAILABLE OR
--     DISCLOSED ARE AS IS.  THE GOVERNMENT MAKES NO EXPRESS OR IMPLIED
--     WARRANTY AS TO ANY MATTER WHATSOVER, INCLUDING THE CONDITIONS OF THE
--     SOFTWARE, DOCUMENTATION OR OTHER INFORMATION RELEASED, MADE AVAILABLE
--     OR DISCLOSED, OR THE OWNERSHIP, MERCHANTABILITY, OR FITNESS FOR A
--     PARTICULAR PURPOSE OF SAID MATERIAL.
--*
--
-- OBJECTIVE
--     Check the requirements of the new 7.6(17.1/1) from Technical
--     Corrigendum 1 (originally discussed as AI95-00083).
--     This new paragraph requires that the initialization of an object with
--     an aggregate does not involve calls to Adjust.
--
-- TEST DESCRIPTION
--     We include several cases of initialization:
--        - Explicit initialization of an object declared by an
--          object declaration.
--        - Explicit initialization of a heap object.
--        - Default initialization of a record component.
--        - Initialization of a formal parameter during a call.
--        - Initialization of a formal parameter during a call with
--          a defaulted parameter.
--        - Lots of nested records, arrays, and pointers.
--     In this test, Initialize should never be called, because we
--     never declare a default-initialized controlled object (although
--     we do declare default-initialized records containing controlled
--     objects, with default expressions for the components).
--     Adjust should never be called, because every initialization
--     is via an aggregate.  Finalize is called, because the objects
--     themselves need to be finalized.
--     Thus, Initialize and Adjust call Failed.
--     In some of the cases, these procedures will not yet be elaborated,
--     anyway.
--
-- CHANGE HISTORY:
--      29 JUN 1999   RAD   Initial Version
--      23 SEP 1999   RLB   Improved comments, renamed, issued.
--      10 APR 2000   RLB   Corrected errors in comments and text, fixed
--                          discriminant error. Fixed so that Report.Test
--                          is called before any Report.Failed call. Added
--                          a marker so that the failed subtest can be
--                          determined.
--      26 APR 2000   RAD   Try to defeat optimizations.
--      04 AUG 2000   RLB   Corrected error in Check_Equal.
--      18 AUG 2000   RLB   Removed dubious main subprogram renames (see AI-172).
--      19 JUL 2002   RLB   Fixed to avoid calling comment after Report.Result.
--
--!

with Ada; use Ada;
with Report; use Report; pragma Elaborate_All(Report);
with Ada.Finalization;
package C761010_1 is
    pragma Elaborate_Body;
    function Square(X: Integer) return Integer;
private
    type TC_Control is new Ada.Finalization.Limited_Controlled with null record;
    procedure Initialize (Object : in out TC_Control);
    procedure Finalize (Object : in out TC_Control);
    TC_Finalize_Called : Boolean := False;
end C761010_1;

package body C761010_1 is
    function Square(X: Integer) return Integer is
    begin
        return X**2;
    end Square;

    procedure Initialize (Object : in out TC_Control) is
    begin
        Test("C761010_1",
             "Check that Adjust is not called"
              & " when aggregates are used to initialize objects");
    end Initialize;

    procedure Finalize (Object : in out TC_Control) is
    begin
        if not TC_Finalize_Called then
            Failed("Var_Strings Finalize never called");
        end if;
        Result;
    end Finalize;

    TC_Test : TC_Control; -- Starts test; finalization ends test.
end C761010_1;

with Ada.Finalization;
package C761010_1.Var_Strings is
    type Var_String(<>) is private;

    Some_String: constant Var_String;

    function "=" (X, Y: Var_String) return Boolean;

    procedure Check_Equal(X, Y: Var_String);
        -- Calls to this are used to defeat optimizations
        -- that might otherwise defeat the purpose of the
        -- test.  I'm talking about the optimization of removing
        -- unused controlled objects.

private

    type String_Ptr is access constant String;

    type Var_String(Length: Natural) is new Finalization.Controlled with
        record
            Comp_1: String_Ptr := new String'(2..Square(Length)-1 => 'x');
            Comp_2: String_Ptr(1..Length) := null;
            Comp_3: String(Length..Length) := (others => '.');
            TC_Lab: Character := '1';
        end record;
    procedure Initialize(X: in out Var_String);
    procedure Adjust(X: in out Var_String);
    procedure Finalize(X: in out Var_String);

    Some_String: constant Var_String
      := (Finalization.Controlled with Length => 1,
          Comp_1 => null,
          Comp_2 => null,
          Comp_3 => "x",
          TC_Lab => 'A');

    Another_String: constant Var_String
      := (Finalization.Controlled with Length => 10,
          Comp_1 => Some_String.Comp_2,
          Comp_2 => new String'("1234567890"),
          Comp_3 => "x",
          TC_Lab => 'B');

end C761010_1.Var_Strings;

package C761010_1.Var_Strings.Types is

    type Ptr is access all Var_String;
    Ptr_Const: constant Ptr;

    type Ptr_Arr is array(Positive range <>) of Ptr;
    Ptr_Arr_Const: constant Ptr_Arr;

    type Ptr_Rec(N_Strings: Natural) is
        record
            Ptrs: Ptr_Arr(1..N_Strings);
        end record;
    Ptr_Rec_Const: constant Ptr_Rec;

private

    Ptr_Const: constant Ptr := new Var_String'
      (Finalization.Controlled with
       Length => 1,
       Comp_1 => null,
       Comp_2 => null,
       Comp_3 => (others => ' '),
       TC_Lab => 'C');

    Ptr_Arr_Const: constant Ptr_Arr :=
      (1 => new Var_String'
       (Finalization.Controlled with
        Length => 1,
        Comp_1 => new String'("abcdefghij"),
        Comp_2 => null,
        Comp_3 => (2..2 => ' '),
        TC_Lab => 'D'));

    Ptr_Rec_Var: Ptr_Rec :=
      (3,
       (1..2 => null,
        3 => new Var_String'
        (Finalization.Controlled with
         Length => 2,
         Comp_1 => new String'("abcdefghij"),
         Comp_2 => null,
         Comp_3 => (2..2 => ' '),
         TC_Lab => 'E')));

    Ptr_Rec_Const: constant Ptr_Rec :=
      (3,
       (1..2 => null,
        3 => new Var_String'
        (Finalization.Controlled with
         Length => 2,
         Comp_1 => new String'("abcdefghij"),
         Comp_2 => null,
         Comp_3 => (2..2 => ' '),
         TC_Lab => 'F')));

    type Arr is array(Positive range <>) of Var_String(Length => 2);

    Arr_Var: Arr :=
      (1 => (Finalization.Controlled with
         Length => 2,
         Comp_1 => new String'("abcdefghij"),
         Comp_2 => null,
         Comp_3 => (2..2 => ' '),
         TC_Lab => 'G'));

    type Rec(N_Strings: Natural) is
        record
            Ptrs: Ptr_Rec(N_Strings);
            Strings: Arr(1..N_Strings) :=
              (others =>
                 (Finalization.Controlled with
                  Length => 2,
                  Comp_1 => new String'("abcdefghij"),
                  Comp_2 => null,
                  Comp_3 => (2..2 => ' '),
                  TC_Lab => 'H'));
        end record;

    Default_Init_Rec_Var: Rec(N_Strings => 10);
    Empty_Default_Init_Rec_Var: Rec(N_Strings => 0);

    Rec_Var: Rec(N_Strings => 2) :=
      (N_Strings => 2,
       Ptrs =>
         (2,
          (1..1 => null,
           2 => new Var_String'
           (Finalization.Controlled with
            Length => 2,
            Comp_1 => new String'("abcdefghij"),
            Comp_2 => null,
            Comp_3 => (2..2 => ' '),
            TC_Lab => 'J'))),
       Strings =>
         (1 =>
            (Finalization.Controlled with
             Length => 2,
             Comp_1 => new String'("abcdefghij"),
             Comp_2 => null,
             Comp_3 => (2..2 => ' '),
             TC_Lab => 'K'),
          others =>
            (Finalization.Controlled with
             Length => 2,
             Comp_1 => new String'("abcdefghij"),
             Comp_2 => null,
             Comp_3 => (2..2 => ' '),
             TC_Lab => 'L')));

    procedure Check_Equal(X, Y: Rec);

end C761010_1.Var_Strings.Types;

package body C761010_1.Var_Strings.Types is

    -- Check that parameter passing doesn't create new objects,
    -- and therefore doesn't need extra Adjusts or Finalizes.

    procedure Check_Equal(X, Y: Rec) is
        -- We assume that the arguments should be equal.
        -- But we cannot assume that pointer values are the same.
    begin
        if X.N_Strings /= Y.N_Strings then
            Failed("Records should be equal (1)");
        else
            for I in 1 .. X.N_Strings loop
                if X.Ptrs.Ptrs(I) /= Y.Ptrs.Ptrs(I) then
                    if X.Ptrs.Ptrs(I) = null or else
                       Y.Ptrs.Ptrs(I) = null or else
                       X.Ptrs.Ptrs(I).all /= Y.Ptrs.Ptrs(I).all then
                       Failed("Records should be equal (2)");
                    end if;
                end if;
                if X.Strings(I) /= Y.Strings(I) then
                    Failed("Records should be equal (3)");
                end if;
            end loop;
        end if;
    end Check_Equal;

    procedure My_Check_Equal
              (X: Rec := Rec_Var;
               Y: Rec :=
      (N_Strings => 2,
       Ptrs =>
         (2,
          (1..1 => null,
           2 => new Var_String'
           (Finalization.Controlled with
            Length => 2,
            Comp_1 => new String'("abcdefghij"),
            Comp_2 => null,
            Comp_3 => (2..2 => ' '),
            TC_Lab => 'M'))),
       Strings =>
         (1 =>
            (Finalization.Controlled with
             Length => 2,
             Comp_1 => new String'("abcdefghij"),
             Comp_2 => null,
             Comp_3 => (2..2 => ' '),
             TC_Lab => 'N'),
          others =>
            (Finalization.Controlled with
             Length => 2,
             Comp_1 => new String'("abcdefghij"),
             Comp_2 => null,
             Comp_3 => (2..2 => ' '),
             TC_Lab => 'O'))))
              renames Check_Equal;
begin

    My_Check_Equal;

    Check_Equal(Rec_Var,
      (N_Strings => 2,
       Ptrs =>
         (2,
          (1..1 => null,
           2 => new Var_String'
           (Finalization.Controlled with
            Length => 2,
            Comp_1 => new String'("abcdefghij"),
            Comp_2 => null,
            Comp_3 => (2..2 => ' '),
            TC_Lab => 'P'))),
       Strings =>
         (1 =>
            (Finalization.Controlled with
             Length => 2,
             Comp_1 => new String'("abcdefghij"),
             Comp_2 => null,
             Comp_3 => (2..2 => ' '),
             TC_Lab => 'Q'),
          others =>
            (Finalization.Controlled with
             Length => 2,
             Comp_1 => new String'("abcdefghij"),
             Comp_2 => null,
             Comp_3 => (2..2 => ' '),
             TC_Lab => 'R'))));

    -- Use the objects to avoid optimizations.

    Check_Equal(Ptr_Const.all, Ptr_Const.all);
    Check_Equal(Ptr_Arr_Const(1).all, Ptr_Arr_Const(1).all);
    Check_Equal(Ptr_Rec_Const.Ptrs(Ptr_Rec_Const.N_Strings).all,
                Ptr_Rec_Const.Ptrs(Ptr_Rec_Const.N_Strings).all);
    Check_Equal(Ptr_Rec_Var.Ptrs(Ptr_Rec_Var.N_Strings).all,
                Ptr_Rec_Var.Ptrs(Ptr_Rec_Var.N_Strings).all);

    if Report.Equal (3, 2) then
       -- Can't get here.
       Check_Equal (Arr_Var(1), Default_Init_Rec_Var.Strings(1));
       Check_Equal (Arr_Var(1), Empty_Default_Init_Rec_Var.Strings(1));
    end if;

end C761010_1.Var_Strings.Types;

with C761010_1.Var_Strings;
with C761010_1.Var_Strings.Types;
procedure C761010_1.Main is
begin
    -- Report.Test is called by the elaboration of C761010_1, and
    -- Report.Result is called by the finalization of C761010_1.
    -- This will happen before any objects are created, and after any
    -- are finalized.
    null;
end C761010_1.Main;

with C761010_1.Main;
procedure C761010 is
begin
    C761010_1.Main;
end C761010;

package body C761010_1.Var_Strings is

    Some_Error: exception;

    procedure Initialize(X: in out Var_String) is
    begin
        Failed("Initialize should never be called");
        raise Some_Error;
    end Initialize;

    procedure Adjust(X: in out Var_String) is
    begin
        Failed("Adjust should never be called - case " & X.TC_Lab);
        raise Some_Error;
    end Adjust;

    procedure Finalize(X: in out Var_String) is
    begin
        Comment("Finalize called - case " & X.TC_Lab);
	C761010_1.TC_Finalize_Called := True;
    end Finalize;

    function "=" (X, Y: Var_String) return Boolean is
        -- Don't check the TC_Lab component, but do check the contents of the
	-- access values.
    begin
        if X.Length /= Y.Length then
            return False;
        end if;
        if X.Comp_3 /= Y.Comp_3 then
            return False;
        end if;
        if X.Comp_1 /= Y.Comp_1 then
            -- Still OK if the values are the same.
            if X.Comp_1 = null or else
               Y.Comp_1 = null or else
               X.Comp_1.all /= Y.Comp_1.all then
               return False;
            --else OK.
            end if;
        end if;
        if X.Comp_2 /= Y.Comp_2 then
            -- Still OK if the values are the same.
            if X.Comp_2 = null or else
               Y.Comp_2 = null or else
               X.Comp_2.all /= Y.Comp_2.all then
               return False;
            end if;
        end if;
        return True;
    end "=";

    procedure Check_Equal(X, Y: Var_String) is
    begin
        if X /= Y then
            Failed("Check_Equal of Var_String");
        end if;
    end Check_Equal;

begin
    Check_Equal(Another_String, Another_String);
end C761010_1.Var_Strings;
